经典卷积神经网络详解(AlexNet、VGG、NiN、GoogLeNet、ResNet、DenseNet、CSPNet、Darknet、EfficientNet、SqueezeNet..) |
您所在的位置:网站首页 › channel wise concatenation › 经典卷积神经网络详解(AlexNet、VGG、NiN、GoogLeNet、ResNet、DenseNet、CSPNet、Darknet、EfficientNet、SqueezeNet..) |
文章目录
经典网络LeNet : 基础图像识别网络 (1998)AlexNet:深度卷积网络 (2012)ZFNet : 大型卷积网络 (2013)VGGNet:使用块的网络 (2014)NiN: 网络中的网络 (2014)GoogLeNet : 含并行连接的网络 (2014)ResNet: 残差网络 (2015)DenseNet: 稠密连接网络 (2017)Darknet-19 (2016)Darknet-53 (2018)EfficientNetV1 (2019)EfficientNetV2 (2021)CSPNet:跨阶段局部网络(2020)
轻量化网络SqueezeNet (2016)MobileNetv1/2/3(2017,2018,2019)ShffleNet (2017)Xception (2017)GhostNet(2020)
参考
经典网络
LeNet : 基础图像识别网络 (1998)
LeNet概述 📃Paper : Gradient-Based Learning Applied to Document Recognition 💻Code: Deci-AI/super-gradients LeNet-5是Yann LeCun等人在多次研究后提出的最终卷积神经网络结构,一般LeNet即指代LeNet-5。LeNet-5是用来处理手写字符的识别问题的。LeNet-5阐述了图像中像素特征之间的相关性能够由参数共享的卷积操作所提取,同时使用卷积、下采样(池化)和非线性映射这样的组合结构,是当前流行的大多是深度图像识别网络的基础。 LeNet的网络结构 输入:32x32的灰度图像,也就是一个通道,那么一个图像就是一个2维的矩阵,没有RGB三个通道。 Layer1:6个大小为5x5的卷积核,步长为1。因此,到这里的输出变成了28x28x6。 Layer2:2x2大小的池化层,使用的是average pooling,步长为2。那么这一层的输出就是14x14x6。 Layer3:16个大小为5x5的卷积核,步长为1。但是,这一层16个卷积核中只有10个和前面的6层相连接。也就是说,这16个卷积核并不是扫描前一层所有的6个通道。如下图,0 1 2 3 4 5这 6个卷积核是扫描3个相邻,然后是6 7 8这3个卷积核是扫描4个相邻,9 10 11 12 13 14这6个是扫描4个非相邻,最后一个15扫描6个。实际上前面的6通道每个都只有10个卷积核扫描到。 Layer4:和第二层一样,2x2大小的池化层,使用的是average pooling,步长为2。 Layer5:全连接卷积层,120个卷积核,大小为1x1。 第四层结束输出为16x5x5,相当于这里16x5x5展开为400个特征,然后使用120神经元去做全连接,如下图所示: Layer6:全连接层,隐藏单元是84个。 Layer7:输出层,输出单元是10个,因为数字识别是0-9。 最终,LeNet-5的总结如下: LeNet小结 卷积网络使用一个3层的序列组合:卷积、下采样 (池化)、非线性映射 (LeNet-5最重要的特 性,奠定了目前深层卷积网络的基础)使用卷积提取空间特征使用映射的空间均值进行下采样使用tanh或sigmoid进行非线性映射多层神经网络 (MLP) 作为最终的分类器层间的稀疏连接矩阵以避免巨大的计算开销 AlexNet:深度卷积网络 (2012)AlexNet 概述: 📃Paper : 《ImageNet Classification with Deep Convolutional Neural Networks》 💻Code: alexnet-pytorch Alex Krizhevsky等人在2012年提出了首个应用于图像分类的卷积神经网络变体AlexNet。他们认为特征本身应该被学习,而且特征应该由多个共同学习的神经网络层组成,每个层都有可学习的参数。 如下图,在网络的最底层,模型学习到了一些类似于传统滤波器的特征抽取器。 AlexNet的更高层建立在这些底层表示的基础上,以表示更大的特征,如眼睛、鼻子、草叶等等。而更高的层可以检测整个物体,如人、飞机、狗或飞盘。最终的隐藏神经元可以学习图像的综合表示,从而使属于不同类别的数据易于区分。 AlexNet使用GPU代替CPU进行运算,使得在可接受的时间范围内模型结构能够更加复杂,它的出现证明了深层卷积神经网络在复杂模型下的有效性。 AlexNet的网络架构: AlexNet和LeNet的架构非常相似,如下图所示。左侧是LeNet, 右侧是AlexNet。 AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。 其次,AlexNet使用ReLU而不是sigmoid作为其激活函数。 下面,让我们深入研究AlexNet的细节。 模型设计 最大池化 在CNN中使用重叠的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避 免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间 会有重叠和覆盖,提升了特征的丰富性。 激活函数 此外,AlexNet将sigmoid激活函数改为更简单的ReLU激活函数。 一方面,ReLU激活函数的计算更简单,它不需要如sigmoid激活函数那般复杂的求幂运算。 另一方面,当使用不同的参数初始化方法时,ReLU激活函数使训练模型更加容易。 当sigmoid激活函数的输出非常接近于0或1时,这些区域的梯度几乎为0,因此反向传播无法继续更新一些模型参数。 相反,ReLU激活函数在正区间的梯度总是1。 因此,如果模型参数没有正确初始化,sigmoid函数可能在正区间内得到几乎为0的梯度,从而使模型无法得到有效的训练。 使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功解决了 Sigmoid在网络较深时的梯度弥散问题,此外,加快了训练速度,因为训练网络使用梯度下降 法,非饱和的非线性函数训练速度快于饱和的非线性函数。虽然ReLU激活函数在很久之前就被 提出了,但是直到AlexNet的出现才将其发扬光大。 Dropout AlexNet通过暂退法(dropout)控制全连接层的模型复杂度,而LeNet只使用了权重衰减。 Dropout虽有单独的论文论述, 但是AlexNet将其实用化,通过实践证实了它的效果。在AlexNet中主要是最后几个全连接层使用 了Dropout。 数据增强 随机地从 256 × 256 256 \times 256 256×256 的原始图像中截取 224 × 224 224 \times 224 224×224 大小的区域(以及水平翻转的镜 像),相当于增加了 ( 256 × 224 ) 2 × 2 = 2048 (256 \times 224) 2 \times 2=2048 (256×224)2×2=2048 倍的数据量。如果没有数据增强,仅靠原始的 数据量,参数众多的CNN会陷入过拟合中,使用了数据增强后可以大大减轻过拟合,提升泛化能 力。进行预测时,则是取图片的四个角加中间共 5 个位置,并进行左右翻转,一共获得10张图 片,对他们进行预测并对10次结果求均值。 使用CUDA加速深度卷积网络的训练 利用GPU强大的并行计算能力,处理神经网络训练时大量 的矩阵运算。AlexNet使用了两块GTX580GPU进行训练,单个GTX580只有3GB显存,这限制了 可训练的网络的最大规模。因此作者将AlexNet分布在两个GPU上,在每个GPU的显存中储存一 半的神经元的参数。 AlexNet的代码实现 import torch from torch import nn from d2l import torch as d2l net = nn.Sequential( # 这里,我们使用一个11*11的更大窗口来捕捉对象。 # 同时,步幅为4,以减少输出的高度和宽度。 # 另外,输出通道的数目远大于LeNet nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2), # 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数 nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2), # 使用三个连续的卷积层和较小的卷积窗口。 # 除了最后的卷积层,输出通道的数量进一步增加。 # 在前两个卷积层之后,汇聚层不用于减少输入的高度和宽度 nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2), nn.Flatten(), # 这里,全连接层的输出数量是LeNet中的好几倍。使用dropout层来减轻过拟合 nn.Linear(6400, 4096), nn.ReLU(), nn.Dropout(p=0.5), nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(p=0.5), # 最后是输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000 nn.Linear(4096, 10))我们构造一个高度和宽度都为224的单通道数据,来观察每一层输出的形状。 X = torch.randn(1, 1, 224, 224) for layer in net: X=layer(X) print(layer.__class__.__name__,'output shape:\t',X.shape) Conv2d output shape: torch.Size([1, 96, 54, 54]) ReLU output shape: torch.Size([1, 96, 54, 54]) MaxPool2d output shape: torch.Size([1, 96, 26, 26]) Conv2d output shape: torch.Size([1, 256, 26, 26]) ReLU output shape: torch.Size([1, 256, 26, 26]) MaxPool2d output shape: torch.Size([1, 256, 12, 12]) Conv2d output shape: torch.Size([1, 384, 12, 12]) ReLU output shape: torch.Size([1, 384, 12, 12]) Conv2d output shape: torch.Size([1, 384, 12, 12]) ReLU output shape: torch.Size([1, 384, 12, 12]) Conv2d output shape: torch.Size([1, 256, 12, 12]) ReLU output shape: torch.Size([1, 256, 12, 12]) MaxPool2d output shape: torch.Size([1, 256, 5, 5]) Flatten output shape: torch.Size([1, 6400]) Linear output shape: torch.Size([1, 4096]) ReLU output shape: torch.Size([1, 4096]) Dropout output shape: torch.Size([1, 4096]) Linear output shape: torch.Size([1, 4096]) ReLU output shape: torch.Size([1, 4096]) Dropout output shape: torch.Size([1, 4096]) Linear output shape: torch.Size([1, 10]) 读取数据集 batch_size = 128 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224) 训练 lr, num_epochs = 0.01, 10 d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) loss 0.327, train acc 0.881, test acc 0.885 4149.6 examples/sec on cuda:0AlexNet小结: 使用ReLU作为CNN的激活函数。训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。在CNN中使用重叠的最大池化。提出了LRN层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并 抑制其他反馈较小的神经元,增强了模型的泛化能力。此方法在之后的VGG中被认为是无效的。AlexNet在训练时增加了大量的图像增强数据,如翻转、裁切和变色。 这使得模型更健壮,更大的样本量有效地减少了过拟合。使用CUDA加速深度卷积网络的训练 ZFNet : 大型卷积网络 (2013)ZFNet概述 📃Paper :《Visualizing and Understanding Convolutional Networks 》 💻Code: pytorch/captum ZFNet在2013年 ILSVRC 图像分类竞赛获得冠军,错误率11.19% ,比2012年的AlexNet降低了5%,ZFNet是由 Matthew D.Zeiler 和 Rob Fergus 在 AlexNet 基础上提出的大型卷积网络。ZFNet解释了为什么卷积神经网络可以在图像分类上表现的如此出色,以及研究了如何优化卷积神经网络。ZFNet提出了一种可视化的技术,通过可视化,我们就可以了解卷积神经网络中间层的功能和分类器的操作,这样就就可以找到较好的模型。ZFNet还进行消融实验来研究模型中的每个组件,它会对模型有什么影响。 ZFNet的网络结构
Z
F
N
e
t
=
(
conv
+
relu
+
maxpooling
)
×
2
+
(
conv
+
relu
)
×
3
+
f
c
×
2
+
softmax
Z F N e t=(\text { conv }+\text { relu }+\text { maxpooling }) \times 2+(\text { conv }+\text { relu }) \times 3+f c \times 2+\text { softmax }
ZFNet=( conv + relu + maxpooling )×2+( conv + relu )×3+fc×2+ softmax ZFNet 仅仅是在 AlexNet 上做了一些调参: 改变了 AlexNet 的第一层,即将卷积核的尺寸大小 11x11 变成 7x7,并且将步长 4 变成了 2。 对卷积结果的可视化 作者将卷积核的计算结果(feature maps)映射回原始的像素空间(映射的方法为反卷积,反池化)并进行可视化。例如,下图Layer1区域最左上角的九宫格代表第一层卷积计算得到的前九张feature maps映射回原图像素空间后的可视化(称为f9)。第一层卷积使用96个卷积核,这意味着会得到96张feature maps,这里的前九张feature maps是指96个卷积核中值最大的9个卷积核对应生成的feature maps(这里称这9个卷积核为k9,即,第一层卷积最关注的前九种特征)。可以发现,这九种特征都是颜色和纹理特征,即蕴含语义信息少的结构性特征。 为了证明这个观点,作者又将数据集中的原始图像裁剪成小图,将所有的小图送进网络中,得到第一层卷积计算后的feature maps。统计能使k9中每个kernel输入计算结果最大的前9张输入小图,即9*9=81张,如下图红框中右下角所示。结果表明刚刚可视化的f9和这81张小图表征的特征是相似的,且一一对应的。由此证明卷积网络在第一层提取到的是一些颜色,纹理特征。 网络中对不同特征的学习速度 如下图所示,横轴表示训练轮数,纵轴表示不同层的feature maps映射回像素空间后的可视化结果: 图像的平移、缩放、旋转对CNN的影响 下图是探究图片平移对卷积模型影响的实验,a1是五张不同的图片经过不同大小的左右平移后的结果。 a2是原始图片与经过平移后的图片分别送进卷积网络后,第一层卷积计算得到的feature maps之间的欧氏距离,可以看出当图片平移0个像素时(即图中横轴=0处),距离最小(等于0)。其他位置随着左右平移,得到的距离都会陡增或陡减。五条彩色曲线分别代表五张不同的原始图片。 a3是原始图片与经过平移后的图片分别送进卷积网络后,第七层卷积计算得到的feature maps之间的欧氏距离,可以看出趋势与a2类似;但是,增减的曲线变换更平缓,这一定程度上说明了网络的深层提取的是高级语义特征,而不是低级的颜色,纹理,空间特征。这种语义信息不会随着平移操作而轻易改变,例如狗的图片平移后还是狗。 这个性质叫做:卷积拥有良好的平移不变性。 最后,a4表示的是原始图片与经过平移后的图片分别送进卷积网络后,卷积网络最后的识别结果。可以看出识别准确率是相对平稳的,且在横轴x=0时,识别准确率较高(此时,图片不平移,识别物体基本在图片中心位置)。 改变卷积核的大小 ZFNet通过对AelxNet可视化发现,由于第一层的卷积核尺寸过大导致某些特征图失效(失效指的是一些值太大或太小的情况,容易引起网络的数值不稳定性,进而导致梯度消失或爆炸。图中的体现是(a)中的黑白像素块)。 此外,由于第一层的步长过大,导致第二层卷积结果出现棋盘状的伪影(例如(d)中第二小图和倒数第三小图)。因此ZFNet做了对应的改进。即将第一层 11X11步长为4的卷积操作变成 7X7步长为2的卷积。 遮挡对卷积模型的影响 ZFNet通过对原始图像进行矩形遮挡来探究其影响,如下图所示:![]() ZFNet的实现代码 import torch.nn as nn import torch # 与AlexNet有两处不同: 1. 第一次的卷积核变小,步幅减小。 2. 第3,4,5层的卷积核数量增加了。 class ZFNet(nn.Module): def __init__(self, num_classes=1000, init_weights=False): super(ZFNet, self).__init__() self.features = nn.Sequential( nn.Conv2d(3, 96, kernel_size=7, stride=2, padding=2), # input[3, 224, 224] output[96, 111, 111] nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2), # output[96, 55, 55] nn.Conv2d(96, 256, kernel_size=5, padding=2), # output[256, 55, 55] nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2), # output[256, 27, 27] nn.Conv2d(256, 512, kernel_size=3, padding=1), # output[512, 27, 27] nn.ReLU(inplace=True), nn.Conv2d(512, 1024, kernel_size=3, padding=1), # output[1024, 27, 27] nn.ReLU(inplace=True), nn.Conv2d(1024, 512, kernel_size=3, padding=1), # output[512, 27, 27] nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2), # output[512, 13, 13] ) self.classifier = nn.Sequential( nn.Dropout(p=0.5), nn.Linear(512 * 13 * 13, 4096), nn.ReLU(inplace=True), nn.Dropout(p=0.5), nn.Linear(4096, 4096), nn.ReLU(inplace=True), nn.Linear(4096, num_classes), ) if init_weights: self._initialize_weights() def forward(self, x): x = self.features(x) x = torch.flatten(x, start_dim=1) x = self.classifier(x) return x def _initialize_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') if m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.Linear): nn.init.normal_(m.weight, 0, 0.01) nn.init.constant_(m.bias, 0) def zfnet(num_classes): model = ZFNet(num_classes=num_classes) return model VGGNet:使用块的网络 (2014)VGGNet概述 📃Paper : 《Very Deep Convolutional Networks for Large-Scale Image Recognition》 💻Code: pytorch/vision VGGNet由牛津大学计算机视觉组合和Google DeepMind公司研究员一起研发的深度卷积神经网络。它探索了卷积神经网络的深度和其性能之间的关系,通过反复的堆叠33的小型卷积核和22的最大池化层,成功的构建了16~19层深的卷积神经网络。VGGNet获得了ILSVRC 2014年比赛的亚军和定位项目的冠军,在top5上的错误率为7.5%。目前为止,VGGNet依然被用来提取图像的特征。 VGGNet全部使用3*3的卷积核和2*2的池化核,通过不断加深网络结构来提升性能。网络层数的增长并不会带来参数量上的爆炸,因为参数量主要集中在最后三个全连接层中。同时,两个3*3卷积层的串联相当于1个5*5的卷积层,3个3*3的卷积层串联相当于1个7*7的卷积层,即3个3*3卷积层的感受野大小相当于1个7*7的卷积层。但是3个3*3的卷积层参数量只有7*7的一半左右,同时前者可以有3个非线性操作,而后者只有1个非线性操作,这样使得前者对于特征的学习能力更强。 VGGNet作者总结出LRN层作用不大,越深的网络效果越好,1*1的卷积也是很有效的,但是没有3*3的卷积效果好,因为3*3的网络可以学习到更大的空间特征。 VGGNet的网络结构 以VGG-16为例,架构图如下: (1) 输入层(Input):图像大小为224×224×3 (2) 卷积层1+ReLU:conv3 - 64(卷积核的数量):kernel size:3 stride:1 pad:1 像素:(224-3+2×1)/1+1=224 输出为224×224×64 64个feature maps 参数: (3×3×3)×64+64=1792 (3) 卷积层2+ReLU:conv3 - 64:kernel size:3 stride:1 pad:1 像素: (224-3+1×2)/1+1=224 输出为224×224×64 64个feature maps 参数: (3×3×64×64)+64=36928 (4) 最大池化层: pool2: kernel size:2 stride:2 pad:0 像素: (224-2)/2 = 112 输出为112×112×64,64个feature maps 参数: 0 (5) 卷积层3+ReLU:.conv3-128:kernel size:3 stride:1 pad:1 像素: (112-3+2×1)/1+1 = 112 输出为112×112×128,128个feature maps 参数: (3×3×64×128)+128=73856 (6) 卷积层4+ReLU:conv3-128:kernel size:3 stride:1 pad:1 像素: (112-3+2×1)/1+1 = 112 输出为112×112×128,128个feature maps 参数: (3×3×128×128)+128=147584 (7) 最大池化层:pool2: kernel size:2 stride:2 pad:0 像素: (112-2)/2+1=56 输出为56×56×128,128个feature maps。 参数: 0 (8) 卷积层5+ReLU:conv3-256: kernel size:3 stride:1 pad:1 像素: (56-3+2×1)/1+1=56 输出为56×56×256,256个feature maps 参数: (3×3×128×256)+256=295168 (9) 卷积层6+ReLU:conv3-256: kernel size:3 stride:1 pad:1 像素: (56-3+2×1)/1+1=56 输出为56×56×256,256个feature maps, 参数: (3×3×256×256)+256=590080 (10) 卷积层7+ReLU:conv3-256: kernel size:3 stride:1 pad:1 像素: (56-3+2×1)/1+1=56 输出为56×56×256,256个feature maps 参数: (3×3×256×256)+256=590080 (11) 最大池化层:pool2: kernel size:2 stride:2 pad:0 像素:(56 - 2)/2+1=28 输出为28×28×256,256个feature maps 参数: 0 (12) 卷积层8+ReLU:conv3-512:kernel size:3 stride:1 pad:1 像素:(28-3+2×1)/1+1=28 输出为28×28×512,512个feature maps 参数: (3×3×256×512)+512=1180160 (13) 卷积层9+ReLU:conv3-512:kernel size:3 stride:1 pad:1 像素:(28-3+2×1)/1+1=28 输出为28×28×512,512个feature maps 参数: (3×3×512×512)+512=2359808 (14) 卷积层10+ReLU:conv3-512:kernel size:3 stride:1 pad:1 像素:(28-3+2×1)/1+1=28 输出为28×28×512,512个feature maps 参数: (3×3×512×512)+512=2359808 (15) 最大池化层:pool2: kernel size:2 stride:2 pad:0,输出为14×14×512,512个feature maps。 像素:(28-2)/2+1=14 输出为14×14×512 参数: 0 (16) 卷积层11+ReLU:conv3-512:kernel size:3 stride:1 pad:1 像素:(14-3+2×1)/1+1=14 输出为14×14×512,512个feature maps 参数: (3×3×512×512)+512=2359808 (17) 卷积层12+ReLU:conv3-512:kernel size:3 stride:1 pad:1 像素:(14-3+2×1)/1+1=14 输出为14×14×512,512个feature maps 参数: (3×3×512×512)+512=2359808 (18) 卷积层13+ReLU:conv3-512:kernel size:3 stride:1 pad:1 像素:(14-3+2×1)/1+1=14 输出为14×14×512,512个feature maps, 参数: (3×3×512×512)+512=2359808 (19) 最大池化层:pool2:kernel size:2 stride:2 pad:0 像素:(14-2)/2+1=7 输出为7×7×512,512个feature maps 参数: 0 (20) 全连接层1+ReLU+Dropout:有4096个神经元或4096个feature maps 像素:1×1×4096 参数:7×7×512×4096 = 102760448 (21) 全连接层2+ReLU+Dropout:有4096个神经元或4096个feature maps 像素:1×1×4096 参数:4096×4096 = 16777216 (22) 全连接层3:有1000个神经元或1000个feature maps 像素:1×1×1000 参数:4096×1000=4096000 (23) 输出层(Softmax):输出识别结果,看它究竟是1000个可能类别中的哪一个。 VGGNet的代码实现 经典卷积神经网络的基本组成部分是下面的这个序列: 带填充以保持分辨率的卷积层;非线性激活函数,如ReLU;汇聚层,如最大汇聚层。 作者使用了带有3×3卷积核、填充为1(保持高度和宽度)的卷积层,和带有2×2汇聚窗口、步幅为2(每个块后的分辨率减半)的最大汇聚层。在下面的代码中,我们定义了一个名为vgg_block的函数来实现一个VGG块。该函数有三个参数,分别对应于卷积层的数量num_convs、输入通道的数量in_channels和输出通道的数量out_channels import torch from torch import nn from d2l import torch as d2l def vgg_block(num_convs, in_channels, out_channels): layers = [] for _ in range(num_convs): layers.append(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)) layers.append(nn.ReLU()) in_channels = out_channels layers.append(nn.MaxPool2d(kernel_size=2,stride=2)) return nn.Sequential(*layers)VGG网络可以分为两部分:第一部分主要由卷积层和汇聚层组成,第二部分由全连接层组成。 VGG神经网络连接几个VGG块(在vgg_block函数中定义)。其中有超参数变量conv_arch。该变量指定了每个VGG块里卷积层个数和输出通道数。全连接模块则与AlexNet中的相同。 原始VGG网络有5个卷积块,其中前两个块各有一个卷积层,后三个块各包含两个卷积层。 第一个模块有64个输出通道,每个后续模块将输出通道数量翻倍,直到该数字达到512。由于该网络使用8个卷积层和3个全连接层,因此它通常被称为VGG-11。 conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))下面的代码实现了VGG-11。可以通过在conv_arch上执行for循环来简单实现。 def vgg(conv_arch): conv_blks = [] in_channels = 1 # 卷积层部分 for (num_convs, out_channels) in conv_arch: conv_blks.append(vgg_block(num_convs, in_channels, out_channels)) in_channels = out_channels return nn.Sequential( *conv_blks, nn.Flatten(), # 全连接层部分 nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, 10)) net = vgg(conv_arch)接下来,我们将构建一个高度和宽度为224的单通道数据样本,以观察每个层输出的形状。 X = torch.randn(size=(1, 1, 224, 224)) for blk in net: X = blk(X) print(blk.__class__.__name__,'output shape:\t',X.shape) Sequential output shape: torch.Size([1, 64, 112, 112]) Sequential output shape: torch.Size([1, 128, 56, 56]) Sequential output shape: torch.Size([1, 256, 28, 28]) Sequential output shape: torch.Size([1, 512, 14, 14]) Sequential output shape: torch.Size([1, 512, 7, 7]) Flatten output shape: torch.Size([1, 25088]) Linear output shape: torch.Size([1, 4096]) ReLU output shape: torch.Size([1, 4096]) Dropout output shape: torch.Size([1, 4096]) Linear output shape: torch.Size([1, 4096]) ReLU output shape: torch.Size([1, 4096]) Dropout output shape: torch.Size([1, 4096]) Linear output shape: torch.Size([1, 10])正如你所看到的,我们在每个块的高度和宽度减半,最终高度和宽度都为7。最后再展平表示,送入全连接层处理。 训练模型由于VGG-11比AlexNet计算量更大,因此我们构建了一个通道数较少的网络,足够用于训练Fashion-MNIST数据集。 ratio = 4 small_conv_arch = [(pair[0], pair[1] // ratio) for pair in conv_arch] net = vgg(small_conv_arch)除了使用略高的学习率外,模型训练过程与 AlexNet类似。 lr, num_epochs, batch_size = 0.05, 10, 128 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224) d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) loss 0.177, train acc 0.934, test acc 0.911 2562.3 examples/sec on cuda:0VGGNet 小结: VGG网络的特点是利用小的尺寸核代替大的卷积核,然后把网络做深。 结构简洁 : 卷积层+ReLU、最大池化层、全连接层、Softmax输出层。 VGGNet的结构十分简洁,由5个卷积层、3个全连接层和1个softmax层构成,层与层之间使用最大池化连接,隐藏层之间使用的激活函数全都是ReLU。使用小卷积核 使用多个3×3卷积堆叠的作用有两个:一是在不影响感受野的前提下减少了参数;二是增加了网络的非线性,从而提升网络的表达能力。使用小滤波器 与AlexNet相比,VGGNet在池化层全部采用的是2×2的小滤波器,stride为2。通道数较多 VGGNet的第一层有64个通道,后面的每一层都对通道进行了翻倍,最多达到了512个通道( 64-128-256-512-512)。由于每个通道都代表着一个feature map,这样就使更多的信息可以被提取出来。图像预处理 训练采用多尺度训练(Multi-scale),将原始图像缩放到不同尺寸 S,然后再随机裁切224x224的图片,并且对图片进行水平翻转和随机RGB色差调整,这样能增加很多数据量,对于防止模型过拟合有很不错的效果。将全连接层转换为卷积层 这个特征是体现在VGGNet的测试阶段。在进行网络测试时,将训练阶段的3个全连接层替换为3个卷积层,使测试得到的网络没有全连接的限制,能够接收任意宽和高的输入。如果后面3个层都是全连接层,那么在测试阶段就只能将测试的图像全部缩放到固定尺寸,这样就不便于多尺度测试工作的开展。![]() NiN的概述 📃Paper : 《Network In Network》 💻Code: pytorch/vision Network In NetWork(NIN) 是由新加坡国立大学的MinLin 等人提出的,在CIFAR-10和CIFAR-100分类任务中达到了SOTA结果。提出mlpconv,引入了1x1卷积和global average pooling,提出Network In Network(NIN),整个模型未使用全连接。模型不够深,迁移能力有待加强。 LeNet、AlexNet和VGG都有一个共同的设计模式:通过一系列的卷积层与汇聚层来提取空间结构特征;然后通过全连接层对特征的表征进行处理。 AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块。 AlexNet和VGG都是先由卷积层构成的模块充分抽取空间特征,再由全连接层构成的模块来输出分类结果。然而,如果使用了全连接层,可能会完全放弃表征的空间结构,且参数量巨大。 因此NiN提出用1*1卷积代替全连接层,串联多个由卷积层和“全连接”层构成的小网络来构建⼀个深层网络。 NiN的网络结构 NIN由三层感知卷积层(MLPConv Layer) 构成。 每一层MLPConv Layer内部由若干个局部的全连接层和非线性激活函数组成,代替了传统卷积层中采用的线性卷积核。 线性卷积层和mlpconv都是将局部感受野(local receptive field)映射到输出特征向量。Mlpconv核使用带非线性激活函数的MLP,跟传统的CNN一样,MLP在各个局部感受野中共享参数的,滑动MLP核可以最终得到输出特征图。 优点是:1. 提供了网络层间映射的一种新可能;2. 增加了网络卷积层的非线性能力。
NiN的代码实现 NiN块卷积层的输入和输出由四维张量组成,张量的每个轴分别对应样本、通道、高度和宽度。 另外,全连接层的输入和输出通常是分别对应于样本和特征的二维张量。 NiN的想法是在每个像素位置(针对每个高度和宽度)应用一个全连接层。 如果我们将权重连接到每个空间位置,我们可以将其视为1×1卷积层,或作为在每个像素位置上独立作用的全连接层。 从另一个角度看,即将空间维度中的每个像素视为单个样本,将通道维度视为不同特征(feature)。 下图说明了VGG和NiN及它们的块之间主要架构差异。 NiN块以一个普通卷积层开始,后面是两个1×1的卷积层。这两个1×1卷积层充当带有ReLU激活函数的逐像素全连接层。 第一层的卷积窗口形状通常由用户设置。 随后的卷积窗口形状固定为1×1。 最初的NiN网络是在AlexNet后不久提出的,显然从中得到了一些启示。 NiN使用窗口形状为11×11、5×5和3×3的卷积层,输出通道数量与AlexNet中的相同。 每个NiN块后有一个最大汇聚层,汇聚窗口形状为3×3,步幅为2。 NiN和AlexNet之间的一个显著区别是NiN完全取消了全连接层。 相反,NiN使用一个NiN块,其输出通道数等于标签类别的数量。最后放一个全局平均汇聚层(global average pooling layer),生成一个对数几率 (logits)。NiN设计的一个优点是,它显著减少了模型所需参数的数量。然而,在实践中,这种设计有时会增加训练模型的时间。 net = nn.Sequential( nin_block(1, 96, kernel_size=11, strides=4, padding=0), nn.MaxPool2d(3, stride=2), nin_block(96, 256, kernel_size=5, strides=1, padding=2), nn.MaxPool2d(3, stride=2), nin_block(256, 384, kernel_size=3, strides=1, padding=1), nn.MaxPool2d(3, stride=2), nn.Dropout(0.5), # 标签类别数是10 nin_block(384, 10, kernel_size=3, strides=1, padding=1), nn.AdaptiveAvgPool2d((1, 1)), # 将四维的输出转成二维的输出,其形状为(批量大小,10) nn.Flatten())我们创建一个数据样本来查看每个块的输出形状。 X = torch.rand(size=(1, 1, 224, 224)) for layer in net: X = layer(X) print(layer.__class__.__name__,'output shape:\t', X.shape) Sequential output shape: torch.Size([1, 96, 54, 54]) MaxPool2d output shape: torch.Size([1, 96, 26, 26]) Sequential output shape: torch.Size([1, 256, 26, 26]) MaxPool2d output shape: torch.Size([1, 256, 12, 12]) Sequential output shape: torch.Size([1, 384, 12, 12]) MaxPool2d output shape: torch.Size([1, 384, 5, 5]) Dropout output shape: torch.Size([1, 384, 5, 5]) Sequential output shape: torch.Size([1, 10, 5, 5]) AdaptiveAvgPool2d output shape: torch.Size([1, 10, 1, 1]) Flatten output shape: torch.Size([1, 10]) 训练模型 lr, num_epochs, batch_size = 0.1, 10, 128 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224) d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) loss 0.363, train acc 0.865, test acc 0.879 3212.2 examples/sec on cuda:0NiN小结 NiN使用由一个卷积层和多个1×1卷积层组成的块。该块可以在卷积神经网络中使用,以允许更多的每像素非线性。NiN去除了容易造成过拟合的全连接层,将它们替换为全局平均汇聚层(即在所有位置上进行求和)。该汇聚层通道数量为所需的输出数量(例如,Fashion-MNIST的输出为10)。移除全连接层可减少过拟合,同时显著减少NiN的参数。NiN的设计影响了许多后续卷积神经网络的设计,如ResNet, Inception,GoogLeNet等网络。 GoogLeNet : 含并行连接的网络 (2014)GoogLeNet的概述 📃Paper : 《Going Deeper with Convolutions》 💻Code: pytorch/vision GoogleNet 网络是14年由 Google 团队提出,斩获该年 ImageNet 竞赛中 Classification Task(分类任务)第一名。之所以名为“GoogLeNet”而非“GoogleNet”,文章说是为了向早期的LeNet致敬。 GoogLeNet吸收了NiN中串联网络的思想,并在此基础上做了改进。 这篇论文的一个重点是解决了什么样大小的卷积核最合适的问题。 毕竟,以前流行的网络使用小到1×1,大到11×11的卷积核。 本文的一个观点是,有时使用不同大小的卷积核组合是有利的。 大量的文献表明可以将稀疏矩阵聚类为较为密集的子矩阵来提高计算性能,据此论文提出了名为Inception 的结构来实现此目的,既能保持网络结构的稀疏性,又能利用密集矩阵的高计算性能。 GoogLeNet的网络结构 Inception结构 Inception 结构的主要思路是怎样用密集成分来近似最优的局部稀疏结构。 作者首先提出下图这样的基本结构:![]() 但是,使用5x5的卷积核仍然会带来巨大的计算量。 为此,文章借鉴NIN2,采用1x1卷积核来进行降维。 例如:上一层的输出为100x100x128,经过具有256个输出的5x5卷积层之后(stride=1,pad=2),输出数据为100x100x256。其中,卷积层的参数为128x5x5x256。假如上一层输出先经过具有32个输出的1x1卷积层,再经过具有256个输出的5x5卷积层,那么最终的输出数据仍为为100x100x256,但卷积参数量已经减少为128x1x1x32 + 32x5x5x256,大约减少了4倍。 具体改进后的Inception Module如下图: GoogLeNet一共使用9个Inception块和全局平均汇聚层的堆叠来生成其估计值。Inception块之间的最大汇聚层可降低维度。 第一个模块类似于AlexNet和LeNet,Inception块的组合从VGG继承,全局平均汇聚层避免了在最后使用全连接层。 GoogLeNet的代码实现 Inception 块 import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l class Inception(nn.Module): # c1--c4是每条路径的输出通道数 def __init__(self, in_channels, c1, c2, c3, c4, **kwargs): super(Inception, self).__init__(**kwargs) # 线路1,单1x1卷积层 self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1) # 线路2,1x1卷积层后接3x3卷积层 self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1) self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1) # 线路3,1x1卷积层后接5x5卷积层 self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1) self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2) # 线路4,3x3最大汇聚层后接1x1卷积层 self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1) self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1) def forward(self, x): p1 = F.relu(self.p1_1(x)) p2 = F.relu(self.p2_2(F.relu(self.p2_1(x)))) p3 = F.relu(self.p3_2(F.relu(self.p3_1(x)))) p4 = F.relu(self.p4_2(self.p4_1(x))) # 在通道维度上连结输出 return torch.cat((p1, p2, p3, p4), dim=1) GoogLeNet模型 现在,我们逐一实现GoogLeNet的每个模块。第一个模块使用64个通道、7×7卷积层。 b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2, padding=1))第二个模块使用两个卷积层:第一个卷积层是64个通道、1×1卷积层;第二个卷积层使用将通道数量增加三倍的3×3卷积层。 这对应于Inception块中的第二条路径。 b2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1), nn.ReLU(), nn.Conv2d(64, 192, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2, padding=1))第三个模块串联两个完整的Inception块。 第一个Inception块的输出通道数为64+128+32+32=256,四个路径之间的输出通道数量比为64:128:32:32=2:4:1:1 。 第二个和第三个路径首先将输入通道的数量分别减少到96/192=1/2 和16/192=1/12,然后连接第二个卷积层。第二个Inception块的输出通道数增加到128+192+96+64=480,四个路径之间的输出通道数量比为128:192:96:64=4:6:3:2。 第二条和第三条路径首先将输入通道的数量分别减少到128/256=1/2和32/256=1/8。 b3 = nn.Sequential(Inception(192, 64, (96, 128), (16, 32), 32), Inception(256, 128, (128, 192), (32, 96), 64), nn.MaxPool2d(kernel_size=3, stride=2, padding=1))第四模块更加复杂, 它串联了5个Inception块,其输出通道数分别是192+208+48+64=512、160+224+64+64=512、128+256+64+64=512、112+288+64+64=528和256+320+128+128=832。 这些路径的通道数分配和第三模块中的类似,首先是含3×3卷积层的第二条路径输出最多通道,其次是仅含1×1卷积层的第一条路径,之后是含5×5卷积层的第三条路径和含3×3最大汇聚层的第四条路径。 其中第二、第三条路径都会先按比例减小通道数。 这些比例在各个Inception块中都略有不同。 b4 = nn.Sequential(Inception(480, 192, (96, 208), (16, 48), 64), Inception(512, 160, (112, 224), (24, 64), 64), Inception(512, 128, (128, 256), (24, 64), 64), Inception(512, 112, (144, 288), (32, 64), 64), Inception(528, 256, (160, 320), (32, 128), 128), nn.MaxPool2d(kernel_size=3, stride=2, padding=1))第五模块包含输出通道数为256+320+128+128=832和384+384+128+128=1024的两个Inception块。 其中每条路径通道数的分配思路和第三、第四模块中的一致,只是在具体数值上有所不同。 需要注意的是,第五模块的后面紧跟输出层,该模块同NiN一样使用全局平均汇聚层,将每个通道的高和宽变成1。 最后我们将输出变成二维数组,再接上一个输出个数为标签类别数的全连接层。 b5 = nn.Sequential(Inception(832, 256, (160, 320), (32, 128), 128), Inception(832, 384, (192, 384), (48, 128), 128), nn.AdaptiveAvgPool2d((1,1)), nn.Flatten()) net = nn.Sequential(b1, b2, b3, b4, b5, nn.Linear(1024, 10))GoogLeNet模型的计算复杂,而且不如VGG那样便于修改通道数。 为了使Fashion-MNIST上的训练短小精悍,我们将输入的高和宽从224降到96,这简化了计算。下面演示各个模块输出的形状变化。 X = torch.rand(size=(1, 1, 96, 96)) for layer in net: X = layer(X) print(layer.__class__.__name__,'output shape:\t', X.shape) Sequential output shape: torch.Size([1, 64, 24, 24]) Sequential output shape: torch.Size([1, 192, 12, 12]) Sequential output shape: torch.Size([1, 480, 6, 6]) Sequential output shape: torch.Size([1, 832, 3, 3]) Sequential output shape: torch.Size([1, 1024]) Linear output shape: torch.Size([1, 10]) 训练模型和以前一样,我们使用Fashion-MNIST数据集来训练我们的模型。在训练之前,我们将图片转换为96×96分辨率。 lr, num_epochs, batch_size = 0.1, 10, 128 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96) d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) loss 0.254, train acc 0.904, test acc 0.885 3570.5 examples/sec on cuda:0GoogLeNet 小结 Inception块相当于一个有4条路径的子网络。它通过不同窗口形状的卷积层和最大汇聚层来并行抽取信息,并使用1×1卷积层减少每像素级别上的通道维数从而降低模型复杂度。GoogLeNet将多个设计精细的Inception块与其他层(卷积层、全连接层)串联起来。其中Inception块的通道数分配之比是在ImageNet数据集上通过大量的实验得来的。GoogLeNet和它的后继者们一度是ImageNet上最有效的模型之一:它以较低的计算复杂度提供了类似的测试精度。 ResNet: 残差网络 (2015)ResNet的概述 📃Paper : Deep Residual Learning for Image Recognition 💻Code: pytorch/vision/models/resnet.py ResNet 网络是在 2015年 由微软实验室中的何凯明等几位大神提出,斩获当年ImageNet竞赛中分类任务第一名,目标检测第一名。获得COCO数据集中目标检测第一名,图像分割第一名。 ResNet的主要贡献: 超深的网络结构(超过1000层)。提出residual(残差结构)模块。使用Batch Normalization 加速训练(丢弃dropout)。ResNet的提出背景: 在ResNet提出之前,所有的神经网络都是通过卷积层和池化层的叠加组成的。 人们认为卷积层和池化层的层数越多,获取到的图片特征信息越全,学习效果也就越好。但是在实际的试验中发现,随着卷积层和池化层的叠加,不但没有出现学习效果越来越好的情况,反而两种问题: 梯度消失和梯度爆炸 梯度消失:若每一层的误差梯度小于1,反向传播时,网络越深,梯度越趋近于0 梯度爆炸:若每一层的误差梯度大于1,反向传播时,网络越深,梯度越来越大退化问题 随着层数的增加,预测效果反而越来越差。如下图所示![]() ![]() ResNet 的结构 Residual 的计算方式 residual结构使用了一种shortcut的连接方式,也可理解为捷径。让特征矩阵隔层相加,注意F(X)和X形状要相同,所谓相加是特征矩阵相同位置上的数字进行相加。 残差块 两种Residual 注:CNN参数个数 = 卷积核尺寸×卷积核深度 × 卷积核组数 = 卷积核尺寸 × 输入特征矩阵深度 × 输出特征矩阵深度 ResNet的代码实现 残差块的实现如下: import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l class Residual(nn.Module): #@save def __init__(self, input_channels, num_channels, use_1x1conv=False, strides=1): super().__init__() self.conv1 = nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1, stride=strides) self.conv2 = nn.Conv2d(num_channels, num_channels, kernel_size=3, padding=1) if use_1x1conv: self.conv3 = nn.Conv2d(input_channels, num_channels, kernel_size=1, stride=strides) else: self.conv3 = None self.bn1 = nn.BatchNorm2d(num_channels) self.bn2 = nn.BatchNorm2d(num_channels) def forward(self, X): Y = F.relu(self.bn1(self.conv1(X))) Y = self.bn2(self.conv2(Y)) if self.conv3: X = self.conv3(X) Y += X return F.relu(Y)如下图所示,此代码生成两种类型的网络: 一种是当use_1x1conv=False 时,应用ReLU非线性函数之前,将输入添加到输出。 另一种是当use_1x1conv=True时,添加通过1×1卷积调整通道和分辨率。 下面我们来查看输入和输出形状一致的情况。 blk = Residual(3,3) X = torch.rand(4, 3, 6, 6) Y = blk(X) Y.shape # torch.Size([4, 3, 6, 6])我们也可以在增加输出通道数的同时,减半输出的高和宽。 blk = Residual(3,6, use_1x1conv=True, strides=2) blk(X).shape # torch.Size([4, 6, 3, 3]) ResNet模型ResNet的前两层跟之前介绍的GoogLeNet中的一样: 在输出通道数为64、步幅为2的7×7卷积层后,接步幅为2的3×3的最大汇聚层。 不同之处在于ResNet每个卷积层后增加了批量规范化层。 b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3), nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2, padding=1))GoogLeNet在后面接了4个由Inception块组成的模块。 ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。 第一个模块的通道数同输入通道数一致。 由于之前已经使用了步幅为2的最大汇聚层,所以无须减小高和宽。 之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。 下面我们来实现这个模块。注意,我们对第一个模块做了特别处理。 def resnet_block(input_channels, num_channels, num_residuals, first_block=False): blk = [] for i in range(num_residuals): if i == 0 and not first_block: blk.append(Residual(input_channels, num_channels,use_1x1conv=True, strides=2)) else: blk.append(Residual(num_channels, num_channels)) return blk接着在ResNet加入所有残差块,这里每个模块使用2个残差块。 b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True)) b3 = nn.Sequential(*resnet_block(64, 128, 2)) b4 = nn.Sequential(*resnet_block(128, 256, 2)) b5 = nn.Sequential(*resnet_block(256, 512, 2))最后,与GoogLeNet一样,在ResNet中加入全局平均汇聚层,以及全连接层输出。 net = nn.Sequential(b1, b2, b3, b4, b5, nn.AdaptiveAvgPool2d((1,1)), nn.Flatten(), nn.Linear(512, 10))每个模块有4个卷积层(不包括恒等映射的1×1卷积层)。 加上第一个7×7 卷积层和最后一个全连接层,共有18层。 因此,这种模型通常被称为ResNet-18。 通过配置不同的通道数和模块里的残差块数可以得到不同的ResNet模型,例如更深的含152层的ResNet-152。 虽然ResNet的主体架构跟GoogLeNet类似,但ResNet架构更简单,修改也更方便。这些因素都导致了ResNet迅速被广泛使用。 下图描述了完整的ResNet-18。 在训练ResNet之前,让我们观察一下ResNet中不同模块的输入形状是如何变化的。 在之前所有架构中,分辨率降低,通道数量增加,直到全局平均汇聚层聚集所有特征。 X = torch.rand(size=(1, 1, 224, 224)) for layer in net: X = layer(X) print(layer.__class__.__name__,'output shape:\t', X.shape) Sequential output shape: torch.Size([1, 64, 56, 56]) Sequential output shape: torch.Size([1, 64, 56, 56]) Sequential output shape: torch.Size([1, 128, 28, 28]) Sequential output shape: torch.Size([1, 256, 14, 14]) Sequential output shape: torch.Size([1, 512, 7, 7]) AdaptiveAvgPool2d output shape: torch.Size([1, 512, 1, 1]) Flatten output shape: torch.Size([1, 512]) Linear output shape: torch.Size([1, 10]) 训练模型同之前一样,我们在Fashion-MNIST数据集上训练ResNet。 lr, num_epochs, batch_size = 0.05, 10, 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96) d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) loss 0.011, train acc 0.997, test acc 0.915 4701.1 examples/sec on cuda:0DenseNet 概述 📃Paper : 《Densely Connected Convolutional Networks》 💻Code: densenet.py 回想一下任意函数的泰勒展开式(Taylor expansion),它把这个函数分解成越来越高阶的项。在x接近0时, f ( x ) = f ( 0 ) + f ′ ( 0 ) x + f ′ ′ ( 0 ) 2 ! x 2 + f ′ ′ ′ ( 0 ) 3 ! x 3 + … f(x)=f(0)+f^{\prime}(0) x+\frac{f^{\prime \prime}(0)}{2 !} x^2+\frac{f^{\prime \prime \prime}(0)}{3 !} x^3+\ldots f(x)=f(0)+f′(0)x+2!f′′(0)x2+3!f′′′(0)x3+… 同样, ResNet将函数展开为 f ( x ) = x + g ( x ) f(\mathbf{x})=\mathbf{x}+g(\mathbf{x}) f(x)=x+g(x) 也就是说,ResNet将 f f f分解为两部分:一个简单的线性项和一个复杂的非线性项。 那么再向前拓展一步,如果我们想将 f f f 拓展成超过两部分的信息呢? 一种方案便是DenseNet。 如图所示,ResNet和DenseNet的关键区别在于,DenseNet输出是连接(用图中的[,]表示)而不是如ResNet的简单相加。 因此,在应用越来越复杂的函数序列后,我们执行从x到其展开式的映射: x → [ x , f 1 ( x ) , f 2 ( [ x , f 1 ( x ) ] ) , f 3 ( [ x , f 1 ( x ) , f 2 ( [ x , f 1 ( x ) ] ) ] ) , … ] \mathbf{x} \rightarrow\left[\mathbf{x}, f_1(\mathbf{x}), f_2\left(\left[\mathbf{x}, f_1(\mathbf{x})\right]\right), f_3\left(\left[\mathbf{x}, f_1(\mathbf{x}), f_2\left(\left[\mathbf{x}, f_1(\mathbf{x})\right]\right)\right]\right), \ldots\right] x→[x,f1(x),f2([x,f1(x)]),f3([x,f1(x),f2([x,f1(x)])]),…] 最后,将这些展开式结合到多层感知机中,再次减少特征的数量。 实现起来非常简单:我们不需要添加术语,而是将它们连接起来。 DenseNet这个名字由变量之间的“稠密连接”而得来,最后一层与之前的所有层紧密相连。 稠密连接如下图所示。 稠密网络主要由2部分构成:稠密块(dense block)和过渡层(transition layer)。 前者定义如何连接输入和输出,而后者则控制通道数量,使其不会太复杂。 DenseNet 的网络结构 DenseNet和Conv、ResNet的对比 在标准的卷积网络中,输入图像经过多次卷积并获得高层特征。![]() ![]() ![]() ![]() DenseNet的网络结构 DenseNet的密集连接方式需要特征图大小保持一致,所以DenseNet网络中使用的是DenseBlock+Transition 的结构。 Dense Block 在DenseBlock中,各个层的特征图大小一致,可以在通道(channel)维度上连接。DenseBlock中的非线性组合函数
H
l
(
⋅
)
H_l(\cdot)
Hl(⋅)采用的是 BN+ ReLU + 3x3 Conv 。输出是一个k个输出通道的特征图。
x
5
=
h
5
(
[
x
0
,
x
1
,
x
2
,
x
3
,
x
4
]
)
x_5=h_5([x_0,x_1,x_2,x_3,x_4])
x5=h5([x0,x1,x2,x3,x4])。 Transition Layer 主要用来连接两个相邻的DenseBlock,并且降低特征图的大小。Transition Layer层包括一个1x1的卷积和2x2的AvgPooling,结构为BN+ReLU+1x1Conv+2x2AvgPooling DenseNet-B (Bottleneck Layers) 由于后面层的输入通道数会非常大,DenseBlock内部可以采用bottleneck Layers来减少为了减少模型的复杂度和大小,主要是原有的结构中增加1x1 Conv,BN+ReLU+1x1Conv+BN+ReLU+3x3Conv, 这种结构也称作DenseNet-B 结构。 其中1x1 Conv的作用是固定输出通道数,达到降维的作用,1×1卷积输出的通道数通常是GrowthRate的4倍。即把上一层输入的
l
×
k
l\times k
l×k个通道数减少成
4
×
k
4 \times k
4×k 个通道数。从而降低特征数量,提升计算效率。 DenseNet-C(Transition Layers) 在一个包含有m个特征图的dense block, Transition Layer产生 θ m \theta m θm 个特征图,其中 θ \theta θ 被看做是压缩系数。 当 θ = 1 \theta =1 θ=1 时,通过过渡层后特征映射数量保持不变。当 θ < 1 \theta |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |